package com.xiang.mall.framework.core.exception

import com.xiang.mall.framework.base.constant.ErrorStatus
import com.xiang.mall.framework.base.exception.BaseException
import com.xiang.mall.framework.base.protocol.ApiResult
import org.hibernate.validator.internal.engine.path.PathImpl
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Controller
import org.springframework.validation.BindException
import org.springframework.web.HttpRequestMethodNotSupportedException
import org.springframework.web.bind.MissingServletRequestParameterException
import org.springframework.web.bind.annotation.*
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
import org.springframework.web.multipart.MultipartException
import java.util.ArrayList
import java.util.HashMap
import javax.validation.ConstraintViolationException

/**
 * @author xiangliyou
 * @email xiangliyou1814@mail.yneusoft.com
 * @date 2019-03-06 14:51
 * 全局异常处理
 */
@ControllerAdvice(annotations = [RestController::class, Controller::class])
class GlobalExceptionHandler {

    companion object {
        private const val DEV_MODE = "dev"
        private const val TEST_MODE = "test"
    }

    @Value("\${spring.profiles.active:dev}")
    lateinit var activeProfile: String

    private val log = LoggerFactory.getLogger("GlobalExceptionHandler")

    private fun getMessage(e: Exception, status: ErrorStatus): String {
        if (activeProfile.equals(DEV_MODE, true) || activeProfile.equals(TEST_MODE, true)) {
            return e.message ?: status.message
        }
        return status.message
    }

    private fun failure(status: ErrorStatus, e: Exception): ApiResult<String> {
        return ApiResult.buildFailure(msg = getMessage(e, status), code = status.value)
    }

    private fun failure(status: ErrorStatus, msg: Any? = null): ApiResult<String> {
        return ApiResult.buildFailure(msg = status.message + ":" + msg?.toString(), code = status.value)
    }

    @ResponseBody
    @ExceptionHandler(BaseException::class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    fun handleException(e: Exception): ApiResult<String> {
        e.printStackTrace()
        log.error(ErrorStatus.INTERNAL_SERVER_ERROR.message + ":" + e.message)
        return failure(ErrorStatus.INTERNAL_SERVER_ERROR, e)
    }


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ConstraintViolationException::class)
    fun handleValidationException(e: ConstraintViolationException): ApiResult<String> {
        log.error(ErrorStatus.ILLEGAL_DATA.message + ":" + e.message)
        val fields = ArrayList<Map<String, Any>>()
        for (cv in e.constraintViolations) {
            val fieldName = (cv.propertyPath as PathImpl).leafNode.asString()
            val message = cv.message
            val field = HashMap<String, Any>()
            field["field"] = fieldName
            field["message"] = message
            fields.add(field)
        }
        return failure(ErrorStatus.ILLEGAL_DATA, fields)
    }

    @ResponseBody
    @ExceptionHandler(BindException::class)
    @ResponseStatus(HttpStatus.OK)
    fun handleBindException(e: BindException): ApiResult<String> {
        log.error(ErrorStatus.ILLEGAL_DATA.message + ":" + e.message)
        val fields = ArrayList<Map<String, Any>>()
        for (error in e.fieldErrors) {
            val field = HashMap<String, Any>()
            field["field"] = error.field
            field["message"] = error.defaultMessage ?: ""
            fields.add(field)
        }
        return failure(ErrorStatus.ILLEGAL_DATA, fields.toString())
    }

    @ResponseBody
    @ExceptionHandler(MultipartException::class)
    @ResponseStatus(HttpStatus.OK)
    fun handleMultipartException(): ApiResult<String> {
        return failure(ErrorStatus.MULTIPART_TOO_LARGE)
    }

    @ResponseBody
    @ExceptionHandler(IllegalArgumentException::class)
    @ResponseStatus(HttpStatus.OK)
    fun handleIllegalArgumentException(e: IllegalArgumentException): ApiResult<String> {
        log.error(ErrorStatus.ILLEGAL_ARGUMENT.message + ":" + e.message)
        return failure(ErrorStatus.ILLEGAL_ARGUMENT, e)
    }

    @ResponseBody
    @ExceptionHandler(MissingServletRequestParameterException::class)
    @ResponseStatus(HttpStatus.OK)
    fun handleMissingServletRequestParameterException(
        e: MissingServletRequestParameterException
    ): ApiResult<String> {
        log.error(ErrorStatus.MISSING_ARGUMENT.message + ":" + e.message)
        return failure(ErrorStatus.MISSING_ARGUMENT, e)
    }

    @ResponseBody
    @ExceptionHandler(MethodArgumentTypeMismatchException::class)
    @ResponseStatus(HttpStatus.OK)
    fun handleMethodArgumentTypeMismatchExceptionException(
        e: MethodArgumentTypeMismatchException
    ): ApiResult<String> {

        log.error(ErrorStatus.ILLEGAL_ARGUMENT_TYPE.message + ":" + e.message)
        return failure(ErrorStatus.ILLEGAL_ARGUMENT_TYPE, e)
    }

    @ResponseBody
    @ExceptionHandler(HttpRequestMethodNotSupportedException::class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    fun handleHttpRequestMethodNotSupportedException(e: HttpRequestMethodNotSupportedException): ApiResult<String> {

        log.error(ErrorStatus.METHOD_NOT_ALLOWED.message + ":" + e.message)
        return failure(ErrorStatus.METHOD_NOT_ALLOWED, e)
    }

    @ResponseBody
    @ExceptionHandler(IllegalStateException::class)
    @ResponseStatus(HttpStatus.OK)
    fun handleIllegalStateException(e: IllegalStateException): ApiResult<String> {
        log.warn("exception", e)
        return failure(ErrorStatus.ILLEGAL_STATE, e)
    }
}